#include<iostream>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/time.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>

char cwd[1024];
int last_code = 0;
int put_num = 0;

// redir
#define NoneRedir 0
#define InputRedir 1
#define OutputRedir 2
#define AppendRedir 3
int redir = NoneRedir;
char* file_name = NULL;

const char* getUserName()
{
    const char* user = getenv("USER");
    if(user != nullptr)
        return user;
    else   
        return "none";
}
char* getHomeName()
{
    char* home = getenv("HOME");
    if(home)
        return home;
    else 
        return (char*)".";
}
const char* getHostName()
{
    const char* host = getenv("HOSTNAME");
    if(host)
        return host;
    else 
        return "none";
}
const char* getCwd()
{
    const char* cwd = getenv("PWD");
    if(cwd)
        return cwd;
    else 
        return "none";
}
int commandLine(char* command, int size)
{
    printf("[%s@%s %s]# ", getUserName(), getHostName(), getCwd());
    char *f_gets = fgets(command, size, stdin);
    if(f_gets == nullptr) 
        return 0;
    // 这里因为我们输出时，总是以回车结尾，而字符串结尾为\0
    command[strlen(command) - 1] = '\0';
    return strlen(command);
}
void commandSplit(char* in, char* out[])
{
    int argc = 0;
    out[argc++] = strtok(in, " ");
    while(out[argc++] = strtok(nullptr, " "));
}
void cd(const char* path)
{
    chdir(path);
    char tmp[1024];
    // 把当前的路径cwd放进tmp中
    getcwd(tmp, sizeof(tmp));
    // 再导入进cwd中，并设置为环境变量
    sprintf(cwd, "PWD=%s", tmp);
    putenv(cwd);
}
int runBashCommand(char* argv[])
{
    // 内建命令可以穷举
    if(strcmp(argv[0], "cd") == 0)
    {
        char* path = nullptr;
        if(argv[1] == nullptr)
            path = getHomeName();
        else 
            path = argv[1];
        
        cd(path);
        return 0;
    }
    else if(strcmp(argv[0], "export") == 0)
    {
        if(argv[1] == nullptr)
            return 0;
        putenv(argv[1]);
        return 0;
    }
    else if(strcmp(argv[0], "echo") == 0)
    {
        if(argv[1] == nullptr)
        {
            printf("\n");
        }
        if(*(argv[1]) == '$' && strlen(argv[1]) > 1)
        {
            char* str = argv[1] + 1;
            if(strcmp(str, "?") == 0)
            {
                printf("%d\n", last_code);
                last_code = 0;
            }
            else{
                if(getenv(str) != nullptr)
                    printf("%s\n", getenv(str));
                else 
                    printf("\n");
            }
        }
        else{
            printf("%s\n", argv[1]);
        }
    }   
    else{
        return 1;
    }

}

void runCommand(char* argv[])
{
    pid_t id = fork();
    if(id == 0)
    {
        int fd = 0;
        if(redir == InputRedir)
        {
            fd = open(file_name, O_CREAT | O_RDONLY, 0666);
            dup2(fd, 0);
        }
        else if(redir == OutputRedir)
        {
            fd = open(file_name, O_CREAT | O_WRONLY | O_TRUNC, 0666);
            dup2(fd, 1);
        }
        else if(redir == AppendRedir)
        {
            fd = open(file_name, O_CREAT| O_WRONLY | O_APPEND, 0666);
            dup2(fd, 1);
        }
        else
        {
            // 不存在重定向无需操作
        }
        // child
        execvp(argv[0], argv);   
        exit(1);
    }
    //father
    int status = 0;
    pid_t rid = waitpid(-1, &status, 0);
    if(rid > 0)
        last_code = WEXITSTATUS(status);
}

void checkRedir(char user_command[], int length)
{
    // 遍历字符串找到重定向
    char* end = user_command + length - 1;
    char* start = user_command;
    
    while(end > start)
    {
        if(*end == '>')
        {
            // 追加重定向
            if(*(end - 1) == '>')
            {
                *(end - 1) = '\0';
                file_name = end + 1;
                while(isspace(*file_name))
                    file_name++;
                redir = AppendRedir;
                break;
            }
            // 输入重定向
            else if(*(end - 1) == ' ')
            {
                *end = '\0';
                file_name = end + 1;
                while(isspace(*file_name))
                    file_name++;
                redir = OutputRedir;
                break;
            }
            else 
            {
                // nothing
                break;
            }
        }
        // 输入重定向
        else if(*end == '<')
        {
            *end = '\0';
            file_name = end + 1;
            while(isspace(*file_name))
                file_name++;
            redir = InputRedir;
            break;

        }
        else 
        {
            end--;
        }
    }
}

int main()
{
    // shell is an endless loop actually
    while(1)
    {
        // 去除上次重定向影响
        redir = NoneRedir;
        file_name = NULL;

        char user_command[1024];
        char* argv[64] = {nullptr};
        // 1. get command from input line
        int n = commandLine(user_command, sizeof(user_command));
        if(n == 0)
            continue;

        // 2. if redir 需要区分多种重定向
        checkRedir(user_command, sizeof(user_command));


        // 3. split the input command to different pieces
        // for example, "ls -a -l" -> "ls" "-a" "-l"
        commandSplit(user_command, argv);

        // 4. bash command should be run before normal command without using child process
        n = runBashCommand(argv);

        // bash command exit this loop
        if(n == 0)  
            continue;
    
        // 5. run the command in you own shell
        runCommand(argv);
    }
}









